// promise====>all/race/allSettle/finnaly
// queue
// prmiseQueue
// cacheRequest
// 请求重试
// lazyman
// flow
// promisify
// co
// 模拟微任务

function co(gen) {
    let g = gen();
    let next = (data) => {
        const { done, value } = g.next(data)
        if (done) return value;
        if (value.then) {
            value.then((item) => next(item))
        } else {
            next(value)
        }
    }
    next()
}
// co(function*() {
//     let a = yield Promise.resolve(1);
//     let b = yield Promise.resolve(2);
//     console.log(a + b)
// })


function promisify(fn) {
    return function(...args) {
        return new Promise((resolve, reject) => {
            function callback(err, ...result) {
                if (err) {
                    reject(err)
                } else {
                    resolve(result.length > 1 ? result : result[0])
                }
            }
            fn.call(this, ...args, callback)
        })
    }
}
// const fs = require('fs');
// const { resolve } = require('path');
// const asyncReadFile = promisify(fs.readFile)

// asyncReadFile('./1.txt').then(item => console.log(item)).catch(err => console.log(1))


Promise.myAll = function(promises) {
    let res = [];
    let count = 0;
    return new Promise((resolve, reject) => {
        for (let i = 0; i < promises.length; i++) {
            Promise.resolve(promises[i]()).then(item => {
                count++
                res[i] = item;
                if (count === promises.length) {
                    resolve(res)
                }
            }).catch(err => reject(err))
        }
    })
}

// Promise.myAll([
//     () => Promise.resolve(1),
//     () => Promise.resolve(2),
//     // () => Promise.reject(3)
// ]).then(res => console.log(res))


Promise.myRace = function(promises) {
    return new Promise((resolve, reject) => {
        for (let i = 0; i < promises.length; i++) {
            Promise.resolve(promises[i]).then(res => {
                resolve(res)
            }).catch((err) => reject(err))
        }
    })
}

const sleep = (delay, tag) => { return new Promise((resolve) => setTimeout(resolve, delay, tag)) }
// Promise.myRace([
//     new Promise((resolve) => setTimeout(resolve, 1100, 'one')),
//     sleep(500, 'two')
// ]).then(item => console.log(item, '----'))

Promise.myAllSettled = function(promises) {
    let res = promises.map(p => {
        return Promise.resolve(p).then(val => ({
            type: 'resolve',
            value: val
        })).catch((err) => ({
            type: 'reject',
            value: err
        }))
    })

    return Promise.all(res)
}

// Promise.myAllSettled([
//     Promise.resolve(1),
//     Promise.reject(2)
// ]).then(res => console.log(res))


Promise.prototype.myFinally = function(fn) {
    let P = this.constructor;
    return this.then(
        (res) => P.resolve(fn()).then(() => res),
        (err) => P.resolve(fn()).then(() => err)
    )
}

// Promise.resolve(1).then(item => console.log(item, '11--1')).myFinally(res => console.log('done'))


function flow(arr) {
    let next = (index) => {
        if (index === arr.length) return;
        let fn = arr[index]()
        if (fn && typeof fn.then === 'function') {
            fn.then(res => next(index + 1))
        } else {
            next(index + 1)
        }
    }
    next(0)
}

// flow([
//     () => console.log(1),
//     () => console.log(2),
//     () => sleep(10000).then(() => console.log(3)),
//     () => console.log(4)
// ])


class Queue {
    constructor() {
        this.list = [];
    }

    add(fn) {
        this.list.push(fn)
        return this
    }
    async run() {
        for (let i = 0; i < this.list.length; i++) {
            await this.list[i]()
        }
    }
}
// const queue = new Queue();
// queue
//     .add(() => sleep(1000).then(()=> console.log('s1')))
//     .add(() => sleep(500).then(()=> console.log('s2')))
//     .add(() => sleep(2000).then(()=> console.log('s3')))
//     .run()


class Lazyman {
    constructor() {
        this.list = []
        setTimeout(() => {
            this.next()
        }, 0)
    }
    log(name) {
        let task = () => setTimeout(() => {
            console.log(name)
            this.next()
        }, 0)
        this.list.push(task)
        return this;
    }
    sleep(time) {
        let task = () => setTimeout(() => {
            console.log(time)
            this.next()
        }, time)
        this.list.push(task)
        return this
    }
    next() {
        let fn = this.list.shift();
        fn && fn()
    }
}
// new Lazyman('jiao').log('1---').sleep(3000).log('2-----')

function request(url, max) {
    return fetch(url).then(item => console.log(item)).catch(() => max > 0 ? request(url, max - 1) : Promise.reject(err))
}


class promiseQueue {
    constructor() {
        this.list = [];
        this.count = 0;
        this.max = 2;
    }

    add(fn) {
        // 加权重的话这里可以排序
        this.list.push(fn)
        this.next();
        return this;
    }

    next() {
        if(this.count >= max || !this.list.length) return;
        let fn = this.list.shift()
        if(!fn) return;
        this.count++
        let res = Promise.resolve(fn())
        res.then(this.done.bind(this)).catch(this.done.bind(this))
    }
    done(){
        this.count--;
        this.next()
    }
}







let max = 2;
let list = [];
let count = 0;
let cacheList = new Map()
function cacheRequest(url, scallback, ecallback) {
    if (cacheList.has(url)) {
        return cacheList.get(url)
    }
    list.push({ url, scallback, ecallback })
    processqueue()
}

async function processqueue() {
    if (count >= max || !list.length) rerurn
    let { url, scallback, ecallback } = list.shift();
    count++
    try {
        let res = await fetch(url)
        let data = await res.json();
        cacheList.set(url, data);
        scallback(data)
    } catch {
        ecallback()
    } finally {
        count--
        processqueue()
    }
}


function MicroTask(fn){
    if(typeof Promise === 'function'){
        Promise.resolve().then(fn)
        return
    }
    if(typeof process.nextTick === "function"){
        process.nextTick(fn)
        return
    }
    if(typeof MutationObserver === 'function'){
        let ob = new MutationObserver(fn);
        let node = document.createTextNode();
        ob.observe(node)
        node.data = 1
        return
    }
}


function isPromiseLike(obj) {  
  return obj != null && typeof obj.then === 'function';  
}
